<?php
defined('IN_XLP') or die('Access Denied');

/* 
 * 2015-02-25 支付宝移动支付接口
 */
class alipayApp {

    //验证通知信息是否为支付宝发送
    const CHECK_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify";
    
    private $partner_id;    //支付宝合作者身份ID 2088开头的纯16位数字
    private $private_key;   //商户私钥
    private $public_key;    //支付宝公钥

    //支付通知URL
    private $notify_url;

    public function __construct() {
        $config = C('app/pay');
        $this->partner_id  = $config['alipay']['partner_id'];
        $this->private_key = $config['alipay']['private_key'];
        $this->public_key  = $config['alipay']['public_key'];

        $this->notify_url  = U('wxpay/uninotify/alipay');
    }

    /*
     * 获取App端发起支付所需参数
     */
    public function getAppParam($p=array()) {

        $p = $this->paramFilter($p);

        $p['service']        = "mobile.securitypay.pay";
        $p['partner']        = $this->partner_id;
        $p['_input_charset'] = "utf-8";
        $p['sign_type']      = "RSA";
        $p['notify_url']     = $this->notify_url;
        $p['payment_type']   = 1;
        $p['seller_id']      = $this->partner_id;

        return $this->getOrderInfo($p);
    }


    /**
     * 订单参数检查过滤
     * @param  array  $p 订单参数
     * @return array     过滤后的参数
     */
    private function paramFilter($p=array()) {

        //外部交易号 最多64字节
        if(!isset($p['out_trade_no'])) {
            throw new Exception("alipay out_trade_no missing");
        }
        //商品名称 最多128字节
        if(isset($p['subject'])) {
            if(strlen($p['subject']) > 128) {
                $p['subject'] = cnsubStr($p['subject'], 120);
            }
        } else {
            throw new Exception("alipay subject missing");
        }
        //单位为RMB-Yuan。取值范围为[0.01,100000000.00]，精确到小数点后两位。
        if(!isset($p['total_fee'])) {
            throw new Exception("alipay total_fee missing");
        }
        //商品详情 最多512字节
        if(isset($p['body'])) {
            if(strlen($p['body']) > 512) {
                $p['body'] = cnsubStr($p['body'], 500);
            }
        } else {
            throw new Exception("alipay body missing");
        }

        return $p;
    }


    /*
     * 获取订单信息并签名
     */
    private function getOrderInfo($p=array()) {
        $str = "";
        //过滤无需签名字段
        foreach ($p as $k => $v) {
            if( in_array($k, array('sign','sign_type')) || $v=="" ) {
                continue;
            }
            $str .= $k.'="'.$v.'"&';
        }

        $sign = $this->rsaSign(rtrim($str,'&'), $this->private_key);

        return $str.'sign="'.urlencode($sign).'"&sign_type="RSA"';
    }

    /*
     * 验证支付宝通知是否合法
     */
    public function checkNotify() {
        if(!isset($_POST['notify_id'])) {
            saveLog("alipay/notify_error", "missing notify id: ".URL." -- ".json_encode($_POST));
            throw new Exception("missing notify id");
        }
        //验证通知方是否为支付宝
        $result = getHttp(self::CHECK_URL."&partner=".$this->partner_id."&notify_id=".$_POST['notify_id']);
        if($result==="true") {
            //验证签名是否正确
            if(!$this->checkNotifySign()) {
                saveLog("alipay/notify_error", "illegal notify sign: ".URL." -- ".json_encode($_POST));
                throw new Exception("illegal notify sign");
            }
        } else {
            saveLog("alipay/notify_error", "illegal notify id: ".URL." -- ".json_encode($_POST));
            throw new Exception("illegal notify id");
        }

        return $_POST;
    }

    /*
     * 验证通知签名
     */
    private function checkNotifySign() {
        ksort($_POST);
        $str = "";
        foreach ($_POST as $k => $v) {
            if( in_array($k, array('sign','sign_type')) || $v=="" ) {
                continue;
            }
            $str .= $k.'='.$v.'&';
        }
        $str = rtrim($str, '&');

        return $this->rsaVerify($str, $this->public_key, $_POST['sign']);
    }

    public function setNotifyURL($url) {
        $this->notify_url = $url;
    }

    public function getNotifyURL() {
        return $this->notify_url;
    }


    /**
     * RSA签名
     * @param $data 待签名数据
     * @param $private_key_path 商户私钥文件路径
     * return 签名结果
     */
    private function rsaSign($data, $private_key_path) {
        $priKey = file_get_contents($private_key_path);
        $res = openssl_get_privatekey($priKey);
        openssl_sign($data, $sign, $res);
        openssl_free_key($res);
        //base64编码
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * RSA验签
     * @param $data 待签名数据
     * @param $ali_public_key_path 支付宝的公钥文件路径
     * @param $sign 要校对的的签名结果
     * return 验证结果
     */
    private function rsaVerify($data, $ali_public_key_path, $sign)  {
        $pubKey = file_get_contents($ali_public_key_path);
        $res = openssl_get_publickey($pubKey);
        $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        openssl_free_key($res);    
        return $result;
    }

}